package org.phnq.stack;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.phnq.babel.*;
import org.phnq.babel.log.*;

/**
 * <<Class summary>>
 *
 * @author Patrick Gostovic &lt;&gt;
 * @version $Rev$
 */
public final class PhnqServlet extends HttpServlet
{
	static
	{
		Environment.init();
	}
	private static Logger log = new Logger(PhnqServlet.class);
	
	private static final String INIT_PARAM_RESOURCE_PATH = "resourcePath";
	private static final String INIT_PARAM_BASE_URI = "baseUri";
	private static final String INIT_PARAM_MINIFY_JS = "minifyJS";
	private static final String INIT_PARAM_DEFAULT = "default";
	
	private static Map<String,String> contentTypes = new HashMap<String,String>();
	static
	{
		contentTypes.put("html", "text/html");
		contentTypes.put("htm", "text/html");
		contentTypes.put("js", "text/javascript");
		contentTypes.put("css", "text/css");
		contentTypes.put("png", "image/png");
		contentTypes.put("gif", "image/gif");
		contentTypes.put("jpeg", "image/jpeg");
		contentTypes.put("jpg", "image/jpeg");
	}
	
	private String defaultPhnqlet = "default";
	private String baseUri = "/";
	private boolean minifyJS = true;
	private String appBasePath = null;

	public PhnqServlet()
	{
	}
	
	public void init(ServletConfig config) throws ServletException
	{
		log.info("Initializing servlet");
		
		appBasePath = config.getServletContext().getRealPath("");
		
		ObjectFactory.addClassAlias("http", "org.phnq.stack.PhnqHttpObjectFactory");

		String baseUriConfig = config.getInitParameter(INIT_PARAM_BASE_URI);
		if(baseUriConfig != null)
		{
			this.baseUri = baseUriConfig;
			if(!this.baseUri.endsWith("/"))
				this.baseUri += "/";
		}
		
		log.info("baseUri: "+baseUri);
		
		String resPath = config.getInitParameter(INIT_PARAM_RESOURCE_PATH);
		if(resPath != null)
		{
			for(String path : resPath.split(":"))
			{
				Environment.addResourcePath(path);
				log.info("adding resource path: "+path);
			}
		}
		
		String defResPath = config.getServletContext().getRealPath("");
		log.info("adding default resource path: "+defResPath);
		Environment.addResourcePath(defResPath);

		String minifyJSStr = config.getInitParameter(INIT_PARAM_MINIFY_JS);
		if(minifyJSStr != null)
			minifyJS = Boolean.parseBoolean(minifyJSStr);
			
		log.info("minifyJS: "+minifyJS);

		String defaultPhnqletConfig = config.getInitParameter(INIT_PARAM_DEFAULT);
		if(defaultPhnqletConfig != null)
			defaultPhnqlet = defaultPhnqletConfig;
	}
	
	protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		// System.out.println("accept-encoding: "+req.getHeader("Accept-Encoding"));
		
		String tParam = req.getParameter("t");
		boolean traceTime = tParam != null && tParam.equals("true");
		
		long start = traceTime ? System.currentTimeMillis() : 0;
		
		String ctxPath = req.getContextPath() + baseUri;
		String reqUri = req.getRequestURI();
		String relUri = reqUri.substring(ctxPath.length());
		
		if(relUri.equals(""))
			relUri = defaultPhnqlet;
		
		Map<String,String> params = new HashMap<String,String>();
		for(Enumeration en=req.getParameterNames(); en.hasMoreElements();)
		{
			String name = (String)en.nextElement();
			params.put(name, req.getParameter(name));
		}
		
		res.setHeader("Content-Type", getContentType(reqUri));
		
		PhnqRequestContext.initForCurrentThread(appBasePath, req, res);
		try
		{
			Processor p = new Processor();
			
			Processor.ProcessorResponse response = p.process(relUri, params);
			
			long clientCacheDur = response.getClientCacheDuration();
			if(clientCacheDur > 0)
				res.setHeader("Cache-Control", "max-age="+clientCacheDur);
				
			if(response.isDeflated())
				res.setHeader("Content-Encoding", "deflate");
				
			OutputStream out = res.getOutputStream();
			InputStream in = response.getBodyStream();
			
			if(in != null)
			{
				byte[] buff = new byte[4096];
				int len;
				while((len=in.read(buff)) != -1)
				{
					out.write(buff, 0, len);
				}
			}
			else
			{
				log.debug("no body stream for "+relUri);
			}
			
			p.dispose();
		}
		catch(BadRequestException ex)
		{
			res.setStatus(400);
		}
		catch(ResourceNotFoundException ex)
		{
			res.setStatus(404);
		}
		catch(OperationForbiddenException ex)
		{
			res.setStatus(403);
		}
		catch(OperationErrorException ex)
		{
			res.setStatus(500);
		}
		catch(Throwable t)
		{
			t.printStackTrace();
		}
		finally
		{
			PhnqRequestContext.disposeForCurrentThread();
		}
		
		if(traceTime)
			System.out.println(""+(System.currentTimeMillis()-start)+"ms - "+reqUri);
	}
	
	private String getContentType(String reqUri)
	{
		int lastDotIdx = reqUri.lastIndexOf(".");
		if(lastDotIdx == -1)
			return "text/html";
			
		String ext = reqUri.substring(lastDotIdx+1);
		
		String contentType = contentTypes.get(ext);
		if(contentType == null)
			contentType = "application/octet-stream";
		
		return contentType;
	}
}
